package gagak.framework.web.param;

import gagak.framework.web.annonation.Nullable;
import gagak.framework.web.asm.AsmMethods;
import gagak.framework.web.paramresovler.StandardReflectionParameterNameDiscoverer;
import gagak.framework.web.reflect.AnnotationAttributes;
import gagak.framework.web.reflect.AnnotationUtils;
import gagak.framework.web.reflect.ClassUtils;
import gagak.framework.web.support.HttpStatus;
import gagak.framework.web.utils.Assert;
import com.g5.framework.web.asm.JavaAssistUtils;
import org.apache.commons.collections.MapUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;

/*
 * @program: ce
 * @description: handler 的具体方法
 * @author: houhong
 * @create: 2022-11-23 21:30
 */
public class HandlerMethod {

    /*
     * 方法所属于的类
     */
    private final Object controller;
    /*
     * controller 类型
     */
    protected final Class<?> controllerType;


    /*
     * 方法类上面注解属性
     */
    private Map<Annotation, AnnotationAttributes> annotationAttributesMap;

    /*
     * 参数上面的ulr 信息
     */
    private String url;

    /*
     * 具体的方法
     */
    private final Method method;

    protected HttpStatus responseStatus;

    private String responseStatusReason;

    /*
     * 方法返回结果信息
     */
    private MethodReturnInfo returnInfo;

    /*
     * 当前方法参数
     */
    private final MethodParameter[] methodParameters;


    protected HandlerMethod(HandlerMethod handlerMethod) {
        Assert.notNull(handlerMethod, "HandlerMethod is required");
        this.controller = handlerMethod.controller;
        this.controllerType = handlerMethod.controllerType;
        this.method = handlerMethod.method;
        this.methodParameters = handlerMethod.getMethodParameters();

    }


    public HandlerMethod(Object controller, Method method) {
        Assert.notNull(controller, "controller is required");
        Assert.notNull(method, "Method is required");
        this.controller = controller;

        this.controllerType = ClassUtils.getUserClass(controller);
        this.method = method;
        this.methodParameters = initMethodParameters();
    }

    //
    public HandlerMethod(Object controller, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {

        Assert.notNull(controller, "controller is required");
        Assert.notNull(methodName, "MethodName is required");
        this.controller = controller;
        this.controllerType = ClassUtils.getUserClass(controller);
        this.method = controller.getClass().getMethod(methodName, parameterTypes);

        this.methodParameters = initMethodParameters();

    }




    public String getResponseStatusReason() {
        return responseStatusReason;
    }

    public void setResponseStatusReason(String responseStatusReason) {
        this.responseStatusReason = responseStatusReason;
    }



    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public MethodReturnInfo getReturnInfo() {
        return returnInfo;
    }

    public void setReturnInfo(MethodReturnInfo returnInfo) {
        this.returnInfo = returnInfo;
    }

    /*
     * 添加信息
     */
    public void addAnnotationAttributesMap(Annotation annotation, AnnotationAttributes attributes) {

        if (MapUtils.isEmpty(annotationAttributesMap)) {
            this.annotationAttributesMap = new LinkedHashMap<Annotation, AnnotationAttributes>(16);
        }
        this.annotationAttributesMap.put(annotation, attributes);
    }

    public Object getController() {
        return controller;
    }

    public Class<?> getControllerType() {
        return controllerType;
    }

    public Map<Annotation, AnnotationAttributes> getAnnotationAttributesMap() {
        return annotationAttributesMap;
    }

    public void setAnnotationAttributesMap(Map<Annotation, AnnotationAttributes> annotationAttributesMap) {
        this.annotationAttributesMap = annotationAttributesMap;
    }

    public Method getMethod() {
        return method;
    }

    public MethodParameter[] getMethodParameters() {
        return methodParameters;
    }

    /*
     * 初始化参数信息
     */
    private MethodParameter[] initMethodParameters() {


        final Method method = this.method;

        //参数类型
        Class<?>[] parameterTypes = method.getParameterTypes();
        int count = parameterTypes.length;
        MethodParameter[] result = new MethodParameter[count];

        try {
            // 获取方法的参数名字
            String[] methodParamNames = StandardReflectionParameterNameDiscoverer.getParameterNames(method);
            //参数注解
            Annotation[][] methodParamAnnonations = AsmMethods.getMethodParamAnnonations(method);
            //获取参数的范型类型
            Type[] genericParameterTypes = method.getGenericParameterTypes();

            int modifiers = method.getModifiers();
            for (int index = 0; index < count; index++) {

                MethodParameter methodParameter = new MethodParameter(method, index);

                //参数真实名字
                String paramrealName = methodParamNames[index];
                //构建参数
                Parameter parameter = new Parameter(paramrealName, modifiers,
                        index);
                //设置参数的基本信息
                methodParameter.setParameter(parameter);
                methodParameter.setParameterName(paramrealName);
                //设置参数类型
                methodParameter.setParameterType(parameterTypes[index]);
                //设置参数范型类型
                Type genericParameterType = genericParameterTypes[index];
                methodParameter.setGenericParameterType(genericParameterType);

                //获取参数上面的注解  第index 的参数
                Annotation[] methodParamAnnonation = methodParamAnnonations[index];
                methodParameter.setParameterAnnotations(methodParamAnnonation);

                for (Annotation annotation : methodParamAnnonation) {
                    AnnotationAttributes annotationAttributes = (AnnotationAttributes) AnnotationUtils.getAnnotationAttributes(annotation);
                    methodParameter.addParamAnnoation(annotationAttributes);
                }
                //放置数据
                result[index] = methodParameter;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }


        return result;
    }

    private MethodReturnInfo initMethodReturnInfo(){

        final Method method = this.method;

        Class<?> returnType = method.getReturnType();
        Type genericReturnType = method.getGenericReturnType();

        MethodReturnInfo returnInfo = new MethodReturnInfo(this.controller,this.method,returnType);
        returnInfo.setReturnTypeType(genericReturnType);

        return  returnInfo;
    }

    // // TODO: 2022/11/25
    private MethodReturnInfo parseReturnInfo() {

        return null;
    }

    public MethodParameter getReturnValueType(@Nullable Object returnValue) {
        return new ReturnValueMethodParameter(returnValue);
    }


    /*
    *  设置一个返回参数
    */
    private class ReturnValueMethodParameter extends HandlerMethodParameter {

        //当前方法放回的结果
        private Object returnValue;

        public ReturnValueMethodParameter(@Nullable Object returnValue) {
            super(-1);
            this.returnValue = returnValue;
        }

        protected ReturnValueMethodParameter(ReturnValueMethodParameter original) {
            super(original);
            this.returnValue = original.returnValue;
        }

        public ReturnValueMethodParameter(int index) {
            super(index);
        }

        @Override
        public Class<?> getParameterType() {
            return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
        }
    }


    protected class HandlerMethodParameter extends SynthesizingMethodParameter {

        public HandlerMethodParameter(int index) {
            super(HandlerMethod.this.method, index);
        }

        protected HandlerMethodParameter(HandlerMethodParameter original) {
            super(original);
        }

        @Override
        public Class<?> getContainingClass() {
            return HandlerMethod.this.controllerType;
        }

        @Override
        public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
            return HandlerMethod.this.getMethodAnnotation(annotationType);
        }

        @Override
        public <T extends Annotation> boolean hasMethodAnnotation(Class<T> annotationType) {
            return HandlerMethod.this.hasMethodAnnotation(annotationType);
        }

        @Override
        public HandlerMethodParameter clone() {
            return new HandlerMethodParameter(this);
        }
    }


    public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
        return AnnotationUtils.findAnnotation(this.method, annotationType);
    }


    public <A extends Annotation> boolean hasMethodAnnotation(Class<A> annotationType) {
        A annotation = AnnotationUtils.findAnnotation(this.method, annotationType);
        return annotation == null;
    }

}